package cc.rengu.igas.opms.core.service.trans.common;

import cc.rengu.igas.opms.common.constant.OpmsParamConstant;
import cc.rengu.igas.opms.common.enums.RespCodeEnum;
import cc.rengu.igas.opms.common.enums.SignTypeEnum;
import cc.rengu.igas.opms.core.service.base.ManageService;
import cc.rengu.igas.opms.facade.base.Header;
import cc.rengu.igas.opms.facade.request.UserLoginRequest;
import cc.rengu.igas.opms.facade.response.UserLoginResponse;
import cc.rengu.igas.share.common.constant.UserSessionConstant;
import cc.rengu.igas.share.core.model.UserInfo;
import cc.rengu.igas.share.core.realize.UserService;
import cc.rengu.igas.share.core.realize.impl.UserServiceImpl;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.dao.SignAcctInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.SignAcctInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.SignAcctInfo;
import cc.rengu.oltp.service.common.entity.SysParam;
import cc.rengu.oltp.service.common.enums.SmsTypeEnum;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.model.BizResponse;
import cc.rengu.oltp.service.realize.SensitiveInfoService;
import cc.rengu.oltp.service.realize.SmsService;
import cc.rengu.oltp.service.realize.SysParamService;
import cc.rengu.oltp.service.realize.impl.SensitiveInfoServiceImpl;
import cc.rengu.oltp.service.realize.impl.SmsServiceImpl;
import cc.rengu.oltp.service.realize.impl.SysParamServiceImpl;
import cc.rengu.oltp.utility.util.*;
import com.alibaba.fastjson.JSON;


import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 用户登陆服务
 * 授信用户采用静默登陆的方式直接登陆
 * 非授信用户可以使用手机号+短信验证码 或 银行卡的方式进行登陆
 * 手机银行跳转过来按照静默登陆的方式进行登陆，但不校验商户必须是授信商户
 *
 * @author keyi
 * @modify by wangcheng
 * @date 2020/3/24.
 */
public class UserLoginService extends ManageService {
    @Override
    protected Object bizBeforeCust() throws Exception {
        UserLoginRequest userLoginRequest = new UserLoginRequest();
        ConvertUtil.convertOutput(userLoginRequest);

        //用户静默登录参数必填校验
        if ("02".equals(userLoginRequest.getLoginMode())) {
            if (StringUtil.isEmptyOrNull(userLoginRequest.getUserId())) {
                rglog.info("静默登录时userId未上送");
                throw new BizException(RespCodeEnum.PARAM_ERROR.getRespCode(), RespCodeEnum.PARAM_ERROR.getRespDesc());
            }
        }
        //判断手机号是否脱敏，若已脱敏，则先恢复脱敏
        if (!StringUtil.isEmptyOrNull(userLoginRequest.getPhoneNo())){
            Pattern pattern = Pattern.compile("^1[0-9]{10}$");
            Matcher matcher = pattern.matcher(userLoginRequest.getPhoneNo());
            if (!matcher.matches()) {
                rglog.info("当前手机号为脱敏后的手机号，需转换成明文!");
                SensitiveInfoService sensitiveInfoService = new SensitiveInfoServiceImpl();
                Map<String, String> keyValueMap = sensitiveInfoService.recovery("phoneNo", userLoginRequest.getPhoneNo(), userLoginRequest.getHeader().getSession());
                String phoneNo = keyValueMap.get(userLoginRequest.getPhoneNo());
                userLoginRequest.setPhoneNo(phoneNo);
            }
        }

        //手机号登录/银行卡登录必填字段校验
        if ("00".equals(userLoginRequest.getLoginMode()) || "01".equals(userLoginRequest.getLoginMode())) {
            if (StringUtil.isEmptyOrNull(userLoginRequest.getPhoneNo())) {
                rglog.info("手机号/银行卡登录时手机号未上传");
                throw new BizException(RespCodeEnum.PARAM_ERROR.getRespCode(), RespCodeEnum.PARAM_ERROR.getRespDesc());
            }
            if (StringUtil.isEmptyOrNull(userLoginRequest.getSmsCode())) {
                rglog.info("手机号/银行卡登录时短信验证码未上传");
                throw new BizException(RespCodeEnum.PARAM_ERROR.getRespCode(), RespCodeEnum.PARAM_ERROR.getRespDesc());
            }
            if (StringUtil.isEmptyOrNull(userLoginRequest.getSmsVerifySeqNum())) {
                rglog.info("手机号/银行卡登录时验证码验证流水号未上传");
                throw new BizException(RespCodeEnum.PARAM_ERROR.getRespCode(), RespCodeEnum.PARAM_ERROR.getRespDesc());
            }
            if ("01".equals(userLoginRequest.getLoginMode())) {
                if (StringUtil.isEmptyOrNull(userLoginRequest.getPayAcctNo())) {
                    rglog.info("银行卡登录时账号未上传");
                    throw new BizException(RespCodeEnum.PARAM_ERROR.getRespCode(), RespCodeEnum.PARAM_ERROR.getRespDesc());
                }
                if (StringUtil.isEmptyOrNull(userLoginRequest.getCertNo())) {
                    rglog.info("银行卡登录时证件号未上传");
                    throw new BizException(RespCodeEnum.PARAM_ERROR.getRespCode(), RespCodeEnum.PARAM_ERROR.getRespDesc());
                }
            }
        }
        return userLoginRequest;
    }

    @Override
    protected BizResponse bizProcessCust(Object request) throws Exception {
        BizResponse<UserLoginResponse> bizResponse = new BizResponse();
        UserLoginResponse userLoginResponse = new UserLoginResponse();
        bizResponse.setRspSysId(AppParamConstant.IGAS);
        UserService userService = new UserServiceImpl();

        UserLoginRequest userLoginRequest = (UserLoginRequest) request;
        String instId = userLoginRequest.getHeader().getInstId();
        String loginMode = userLoginRequest.getLoginMode();
        String phoneNo = userLoginRequest.getPhoneNo();
        String platMchntNo = userLoginRequest.getPlatMchntNo();
        String mchntBelongInst = "";
        String userInst = instId;
        String signInst = instId;
        String signType = SignTypeEnum.BANK_CLASS_SIGN.getSignTypeCode();

        if (null != platMchntInfo) {
            signType = platMchntInfo.getSignType();
            mchntBelongInst = platMchntInfo.getSignInstId();
            if (SignTypeEnum.PLAT_CLASS_SIGN.getSignTypeCode().equals(platMchntInfo.getSignType())) {
                userInst = platMchntInfo.getPlatMchntNo();
                signInst = platMchntInfo.getPlatMchntNo();
            }
        }
        String sessionId = RandomUtil.getUUID();
        SysParamService sysParamService = new SysParamServiceImpl();
        SysParam sysParam = sysParamService.getSysParamInfo(instId, OpmsParamConstant.OPMS_SYS_PARAM, OpmsParamConstant.SESSION_EXPIRY_TIMES);
        int sessionExpiryTime = sysParam == null || StringUtil.isEmptyOrNull(sysParam.getParamValue()) ? OpmsParamConstant.SESSION_DEFAULT_TIMES : Integer.parseInt(sysParam.getParamValue());

        // 静默登录
        if (OpmsParamConstant.GRANT_USER_LOGIN_MODE.equals(loginMode)) {
            /*查询用户主体已在*/
            UserInfo userInfo = userService.getUserInfoByUserId(instId, userLoginRequest.getUserId());
            if (null == userInfo || !"00".equals(userInfo.getUserStatus())) {
                rglog.debug("当前用户不存在");
                throw new BizException(RespCodeEnum.USER_NOT_EXIT_ERROR.getRespCode(), RespCodeEnum.USER_NOT_EXIT_ERROR.getRespDesc());
            }
            rglog.debug("将用户信息设置到Redis缓存:<{}>", JSON.toJSONString(userInfo));
            userInfo.setUserInst(userInst);
            RedisUtil.hashSet(sessionId, instId + userLoginRequest.getUserId(), JSON.toJSONString(userInfo), sessionExpiryTime);

            //设置用户会话相关信息
            rglog.debug("生成用户会话缓存信息...");
            RedisUtil.hashSet(sessionId, UserSessionConstant.SENSITIVE_KEY, RedisUtil.hashGet(userLoginRequest.getHeader().getSession(), UserSessionConstant.SENSITIVE_KEY));
            RedisUtil.hashSet(sessionId, UserSessionConstant.SIGNATURE_KEY, RedisUtil.hashGet(userLoginRequest.getHeader().getSession(), UserSessionConstant.SIGNATURE_KEY));
            RedisUtil.hashSet(sessionId, UserSessionConstant.GRANT_CREDIT_FLAG, AppParamConstant.NO);
            RedisUtil.hashSet(sessionId, UserSessionConstant.TRANS_MCHNT_NO, platMchntNo);
            RedisUtil.hashSet(sessionId, UserSessionConstant.MCHNT_SIGN_INST, mchntBelongInst);
            RedisUtil.hashSet(sessionId, UserSessionConstant.SIGN_INST, signInst);
            RedisUtil.hashSet(sessionId, UserSessionConstant.SIGN_TYPE, signType);
            rglog.debug("删除旧的会话缓存");
            RedisUtil.onceDel(userLoginRequest.getHeader().getSession());

            //设置响应结果
            Header header = new Header();
            BeanUtil.beanCopy(userLoginRequest.getHeader(), header);
            header.setSession(sessionId);
            userLoginResponse.setHeader(header);
            userLoginResponse.setUserId(userLoginRequest.getUserId());
            userLoginResponse.setPhoneNo(userInfo.getMobileNo());
            userLoginResponse.setUserName(userInfo.getUserName());
            bizResponse.setRespCode(RespCodeEnum.TRANS_SUCCESS.getRespCode());
            bizResponse.setRespDesc(RespCodeEnum.TRANS_SUCCESS.getRespDesc());
            bizResponse.setResult(userLoginResponse);
            return bizResponse;
        }

        UserInfo userInfo;
        if (OpmsParamConstant.USER_LOGIN_MODE_PHONENO.equals(loginMode)) {
            /*手机号模式登录*/
            rglog.debug("用户选择手机号登陆,loginMode:<{}>", loginMode);
            List<UserInfo> userInfoList = userService.getUserInfoByPhoneNo(instId, userLoginRequest.getPhoneNo());
            if (null == userInfoList || userInfoList.isEmpty()) {
                rglog.debug("当前用户不存在");
                throw new BizException(RespCodeEnum.USER_NOT_EXIT_ERROR.getRespCode(), RespCodeEnum.USER_NOT_EXIT_ERROR.getRespDesc());
            }
            //验证当前手机号是否存在多个用户在使用，若多个用户均在使用则需要验证证件类型和证件号一致的用户
            rglog.debug("验证当前手机号是否存在多个用户在使用,userInfoSize:<{}>，若多个用户均在使用则需要验证证件类型和证件号一致的用户", userInfoList.size());
            if (userInfoList.size() != 1) {
                rglog.info("当前手机号存在多个用户在使用,需要验证是否存在与上送的证件类型证件号一致的用户信息");
                if (StringUtil.isEmptyOrNull(userLoginRequest.getCertType()) || StringUtil.isEmptyOrNull(userLoginRequest.getCertNo())) {
                    rglog.error("手机号验证时存在多个用户使用同一个手机号:<{}>,此时须上送证件类型及证件号进行辅助校验!", phoneNo);
                    bizResponse.setRespCode(RespCodeEnum.MULTIPLE_USERS.getRespCode());
                    bizResponse.setRespCode(RespCodeEnum.MULTIPLE_USERS.getRespDesc());
                    bizResponse.setResult(userLoginResponse);
                    return bizResponse;
                } else {
                    rglog.debug("手机号验证时存在多个用户使用同一个手机号,根据上送的证件类型和证件号获取当前登录用户信息");
                    List<UserInfo> userInfos = userInfoList.stream().filter(item -> userLoginRequest.getCertType().equals(item.getCertType())
                            && userLoginRequest.getCertNo().equals(item.getCertNo())).collect(Collectors.toList());
                    if (userInfos.isEmpty()) {
                        rglog.info("不存在当前证件类型:<{}>,证件号:<{}>，手机号:<{}>注册的有效用户", userLoginRequest.getCertType(), userLoginRequest.getCertNo(), phoneNo);
                        throw new BizException(RespCodeEnum.USER_INVALID.getRespCode(), RespCodeEnum.USER_INVALID.getRespDesc());
                    }
                    userInfo = userInfos.get(0);
                }
            } else {
                userInfo = userInfoList.get(0);
            }

            //校验短信验证码
            rglog.debug("短信验证码校验手机号:<{}>", userLoginRequest.getPhoneNo());
            String smsCode = userLoginRequest.getSmsCode();
            String smsVerifySeqNum = userLoginRequest.getSmsVerifySeqNum();
            SmsService smsService = new SmsServiceImpl();
            smsService.verifySmsCode(instId, SmsTypeEnum.USER_LOGIN.getSmsType(), smsCode, smsVerifySeqNum, userLoginRequest.getPhoneNo());
        } else if (OpmsParamConstant.USER_LOGIN_MODE_CARD.equals(loginMode)) {
            rglog.debug("用户选择银行卡登陆,loginMode:<{}>", loginMode);
            String certNo = userLoginRequest.getCertNo();
            String payAcctNo = userLoginRequest.getPayAcctNo();
            SignAcctInfoMapper signAcctInfoMapper = new SignAcctInfoMapperImpl();
            List<SignAcctInfo> signAcctInfos = signAcctInfoMapper.selectSignAcctInfoBySignAcctNo(instId, payAcctNo);
            if (null == signAcctInfos || signAcctInfos.size() == OpmsParamConstant.INT_ZERO) {
                rglog.warn("该用户在该商户不存在有效的签约关系，instId:<{}>,signType:<{}>,signInst:<{}>,payAcctNo:<{}>", instId, signType, signInst, payAcctNo);
                throw new BizException(RespCodeEnum.IDENTITY_INFORM_DISACCORD.getRespCode(), RespCodeEnum.IDENTITY_INFORM_DISACCORD.getRespCode());
            } else {
                String requestPhoneNo = userLoginRequest.getPhoneNo();
                rglog.debug("上送的手机号:<{}>", requestPhoneNo);
                String finalSignType = signType;
                String finalSignInst = signInst;
                signAcctInfos = signAcctInfos.stream().filter(item -> item.getSignType().equals(finalSignType) &&
                        item.getSignInst().equals(finalSignInst) && item.getSignStatus().equals(AppParamConstant.YES)).collect(Collectors.toList());
                for (SignAcctInfo signAcctInfo : signAcctInfos) {
                    if (!signAcctInfo.getCertNo().endsWith(certNo)) {
                        rglog.warn("签约证件号码与上传证件号码不一致,signCertNo:<{}>,certNo:<{}>", signAcctInfo.getCertNo(), certNo);
                        throw new BizException(RespCodeEnum.CERT_NO_DIFF.getRespCode(), RespCodeEnum.CERT_NO_DIFF.getRespDesc());
                    }
                    if (!signAcctInfo.getReserveMobileNo().equals(requestPhoneNo)) {
                        rglog.warn("上送手机号与签约预留手机号不一致，userLoginPhone:<{}>,reserveMobileNo:<{}>", userLoginRequest.getPhoneNo(), signAcctInfo.getReserveMobileNo());
                        throw new BizException(RespCodeEnum.USER_INFO_MISMATCH.getRespCode(), RespCodeEnum.USER_INFO_MISMATCH.getRespDesc());
                    }
                }

                //校验短信验证码
                String smsCode = userLoginRequest.getSmsCode();
                String smsVerifySeqNum = userLoginRequest.getSmsVerifySeqNum();
                SmsService smsService = new SmsServiceImpl();
                smsService.verifySmsCode(instId, SmsTypeEnum.USER_LOGIN.getSmsType(), smsCode, smsVerifySeqNum, requestPhoneNo);

                //银行卡登录返回用户登录手机号
                userInfo = userService.getUserInfoByUserId(instId, signAcctInfos.get(0).getUserId());
                if (null == userInfo || OpmsParamConstant.USER_INFO_USER_STATUS_CANCEL.equals(userInfo.getUserStatus())) {
                    rglog.info("用户信息不存在或用户已注销");
                    throw new BizException(RespCodeEnum.USER_NOT_EXIT_ERROR.getRespCode(), RespCodeEnum.USER_NOT_EXIT_ERROR.getRespDesc());
                }
                userLoginResponse.setPhoneNo(userInfo.getMobileNo());
            }
        } else {
            rglog.error("不识别的登陆模式，loginMode:<{}>", loginMode);
            throw new BizException(RespCodeEnum.LOGIN_MODE_ERR.getRespCode(), RespCodeEnum.LOGIN_MODE_ERR.getRespDesc());
        }

        rglog.debug("将用户信息设置到Redis缓存:<{}>", JSON.toJSONString(userInfo));
        userInfo.setUserInst(userInst);
        RedisUtil.hashSet(sessionId, instId + userInfo.getUserId(), JSON.toJSONString(userInfo), sessionExpiryTime);

        //设置用户会话相关信息
        rglog.debug("生成用户会话缓存信息...");
        RedisUtil.hashSet(sessionId, UserSessionConstant.SENSITIVE_KEY, RedisUtil.hashGet(userLoginRequest.getHeader().getSession(), UserSessionConstant.SENSITIVE_KEY));
        RedisUtil.hashSet(sessionId, UserSessionConstant.SIGNATURE_KEY, RedisUtil.hashGet(userLoginRequest.getHeader().getSession(), UserSessionConstant.SIGNATURE_KEY));
        RedisUtil.hashSet(sessionId, UserSessionConstant.GRANT_CREDIT_FLAG, AppParamConstant.NO);
        RedisUtil.hashSet(sessionId, UserSessionConstant.TRANS_MCHNT_NO, platMchntNo);
        RedisUtil.hashSet(sessionId, UserSessionConstant.MCHNT_SIGN_INST, mchntBelongInst);
        RedisUtil.hashSet(sessionId, UserSessionConstant.SIGN_INST, signInst);
        RedisUtil.hashSet(sessionId, UserSessionConstant.SIGN_TYPE, signType);
        rglog.debug("删除旧的会话缓存");
        RedisUtil.onceDel(userLoginRequest.getHeader().getSession());

        //设置响应结果
        rglog.debug("开始组装响应信息");
        Header header = new Header();
        BeanUtil.beanCopy(userLoginRequest.getHeader(), header);
        rglog.debug("报文头转换结束");
        header.setSession(sessionId);
        userLoginResponse.setHeader(header);
        userLoginResponse.setUserId(userInfo.getUserId());
        userLoginResponse.setUserName(userInfo.getUserName());
        bizResponse.setRespCode(RespCodeEnum.TRANS_SUCCESS.getRespCode());
        bizResponse.setRespDesc(RespCodeEnum.TRANS_SUCCESS.getRespDesc());
        bizResponse.setResult(userLoginResponse);
        return bizResponse;
    }

    @Override
    protected void bizAfterCust(Object response) throws Exception {
        rglog.debug("开始转换响应信息");
        BizResponse bizResponse = (BizResponse) response;
        UserLoginResponse userLoginResponse = (UserLoginResponse) bizResponse.getResult();
        ConvertUtil.convertInput(userLoginResponse);
    }
}
